/*
  author Sylvain Bertrand <digital.ragnarok@gmail.com>
  Protected by GNU Affero GPL v3 with some exceptions.
  See README at root of alga tree.
*/
#include <linux/pci.h>
#include <linux/interrupt.h>
#include <linux/cdev.h>

#include "types.h"
#include "ba.h"
#include "ucode.h"
#include "cp.h"
#include "gpu.h"
#include "irq.h"
#include "drv.h"

#include "regs.h"

void ih_enable(struct pci_dev *dev)
{
	u32 ih_ctl;
	u32 ih_rb_ctl;

	ih_ctl = rr32(dev, IH_CTL);
	ih_rb_ctl = rr32(dev, IH_RB_CTL);

	ih_ctl |= ENABLE_INTR;
	ih_rb_ctl |= IH_RB_ENABLE;
	wr32(dev, ih_ctl, IH_CTL);
	wr32(dev, ih_rb_ctl, IH_RB_CTL);
}

void ih_disable(struct pci_dev *dev)
{
	u32 ih_rb_ctl;
	u32 ih_ctl;

	ih_rb_ctl = rr32(dev, IH_RB_CTL);
	ih_ctl = rr32(dev, IH_CTL);

	ih_rb_ctl &= ~IH_RB_ENABLE;
	ih_ctl &= ~ENABLE_INTR;

	/* works even if ucode in not loaded */
	wr32(dev, ih_rb_ctl, IH_RB_CTL);
	wr32(dev, ih_ctl, IH_CTL);

	wr32(dev, 0, IH_RB_RPTR);
	wr32(dev, 0, IH_RB_WPTR);
}

/*
 *  o ih ring size is 2^IH_RING_LOG2_DWS(14) dwords (16 * 4096 bytes)
 *  o writeback page is put right at the start of bus aperture after vram
 *    (an a cpu page boundary)
 *  o ih ring is put right after the writeback page
 */
#define WB_IH_WPTR_OFFSET 2048
void ih_init(struct pci_dev *dev)
{
	u32 intr_ctl;
	gpu_addr_t ih;
	gpu_addr_t wb_page;
	u32 ih_rb_ctl;
	u32 ih_ctl;

	ucode_rlc_program(dev);

	wb_page = rr32(dev, CFG_MEM_SZ) * 1024 * 1024;
	ih = wb_page + GPU_PAGE_SZ;

	/*
	 * setup interrupt control
	 * set dummy read address to ring address
	 */
	wr32(dev, ih >> 8, INTR_CTL2); /* 256 bytes block index */

	intr_ctl = rr32(dev, INTR_CTL);
	/*
	 * IH_DUMMY_RD_OVERRIDE=0 - dummy read disabled with msi, enabled
	 *                          without msi
	 * IH_DUMMY_RD_OVERRIDE=1 - dummy read controlled by IH_DUMMY_RD_EN
	 */
	intr_ctl &= ~IH_DUMMY_RD_OVERRIDE; /* GPU should disable dummy read */

	/* IH_REQ_NONSNOOP_EN=1 if ring is in non-cacheable memory, e.g. vram */
	intr_ctl &= ~IH_REQ_NONSNOOP_EN; /* we are in bus aperture */
	wr32(dev, intr_ctl, INTR_CTL);

	wr32(dev, ih >> 8, IH_RB_BASE); /* 256 bytes block index */

	ih_rb_ctl = (IH_WPTR_OVERFLOW_ENABLE | IH_WPTR_OVERFLOW_CLEAR
					| IH_IB_LOG2_DWS(IH_RING_LOG2_DWS)
					| IH_WPTR_WRITEBACK_ENABLE);

	/*
	 * use the page right after the ring as writeback page
	 * set the writeback address whether it's enabled or not
	 */
	wr32(dev, (wb_page + WB_IH_WPTR_OFFSET) & 0xfffffffc,
							IH_RB_WPTR_ADDR_LO);
	wr32(dev, upper_32_bits(wb_page + WB_IH_WPTR_OFFSET) & 0xff,
							IH_RB_WPTR_ADDR_HI);
	wr32(dev, ih_rb_ctl, IH_RB_CTL);

	wr32(dev, 0, IH_RB_RPTR);
	wr32(dev, 0, IH_RB_WPTR);

	/* default settings for IH_CTL (disabled at first) */
	ih_ctl = MC_WRREQ_CREDIT(0x10) | MC_WR_CLEAN_CNT(0x10) | RPTR_REARM;

	wr32(dev, ih_ctl, IH_CTL);
}
